home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 6912 / 6912.xpi / chrome / quickdrag.jar / content / quickdrag.js next >
Text File  |  2009-05-01  |  13KB  |  429 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is QuickDrag.
  15.  *
  16.  * The Initial Developer of the Original Code is Kai Liu.
  17.  * Portions created by the Initial Developer are Copyright (C) 2008-2009
  18.  * the Initial Developer. All Rights Reserved.
  19.  *
  20.  * Contributor(s):
  21.  *   Kai Liu <kliu@code.kliu.org>
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. var QuickDragListener =
  38. {
  39.     // The name of the event that we should listen to for drops
  40.     _drop: "drop",
  41.  
  42.     handleEvent: function( evt )
  43.     {
  44.         var panel = gBrowser.mPanelContainer;
  45.  
  46.         switch (evt.type)
  47.         {
  48.             case "load":
  49.             {
  50.                 // Prior to the landing of the WHATWG drag API in Gecko 1.9.1,
  51.                 // the event name was "dragdrop"
  52.                 if ("getDragData" in nsDragAndDrop)
  53.                     this._drop = "dragdrop";
  54.  
  55.                 window.removeEventListener(evt.type, this, false);
  56.                 panel.addEventListener("dragstart", this, false);
  57.                 panel.addEventListener("dragover", this, false);
  58.                 panel.addEventListener(this._drop, this, false);
  59.                 break;
  60.             }
  61.  
  62.             case "unload":
  63.             {
  64.                 window.removeEventListener(evt.type, this, false);
  65.                 panel.removeEventListener("dragstart", this, false);
  66.                 panel.removeEventListener("dragover", this, false);
  67.                 panel.removeEventListener(this._drop, this, false);
  68.                 break;
  69.             }
  70.  
  71.             case "dragstart":
  72.             {
  73.                 QuickDrag.dragstart(evt);
  74.                 break;
  75.             }
  76.  
  77.             case "dragover":
  78.             {
  79.                 QuickDrag.dragover(evt);
  80.                 break;
  81.             }
  82.  
  83.             case this._drop:
  84.             {
  85.                 QuickDrag.dragdrop(evt);
  86.                 break;
  87.             }
  88.         }
  89.     }
  90. };
  91.  
  92. var QuickDrag =
  93. {
  94.     /**
  95.      * For a variety of reasons, the nsDragAndDrop JS wrapper is not suitable
  96.      * for this extension, but there are some pieces of nsDragAndDrop that are
  97.      * useful; these parts have been wrapped inside _session, _getDragData,
  98.      * and _securityCheck.
  99.      **/
  100.  
  101.     // We want to know the "true" source of the drag, which we can no longer
  102.     // reliably get from the drag session in Gecko 1.9.1
  103.     _sourceNode: null,
  104.  
  105.     // Wrapper for nsDragAndDrop.mDragSession
  106.     get _session( )
  107.     {
  108.         if (!nsDragAndDrop.mDragSession)
  109.             nsDragAndDrop.mDragSession = nsDragAndDrop.mDragService.getCurrentSession();
  110.  
  111.         return(nsDragAndDrop.mDragSession);
  112.     },
  113.  
  114.     // Wrapper for nsDragAndDrop.js's data retrieval; see nsDragAndDrop.drop
  115.     _getDragData: function( aEvent )
  116.     {
  117.         var data = "";
  118.         var type = "text/unicode";
  119.  
  120.         if ("dataTransfer" in aEvent)
  121.         {
  122.             // Gecko 1.9.1 and newer: WHATWG drag-and-drop
  123.  
  124.             // Try to get text/x-moz-url, if possible
  125.             data = aEvent.dataTransfer.getData("text/x-moz-url");
  126.  
  127.             if (data.length != 0)
  128.                 type = "text/x-moz-url";
  129.             else
  130.                 data = aEvent.dataTransfer.getData("text/plain");
  131.         }
  132.         else if ("getDragData" in nsDragAndDrop)
  133.         {
  134.             // Gecko 1.9.0 and older: wrapper for nsDragAndDrop.getDragData
  135.  
  136.             var flavourSet = new FlavourSet();
  137.             flavourSet.appendFlavour("text/x-moz-url");
  138.             flavourSet.appendFlavour("text/unicode");
  139.  
  140.             var transferDataSet = nsTransferable.get(flavourSet, nsDragAndDrop.getDragData, true);
  141.  
  142.             data = transferDataSet.first.first.data;
  143.             type = transferDataSet.first.first.flavour.contentType;
  144.         }
  145.  
  146.         return({ data: data, type: type });
  147.     },
  148.  
  149.     // Wrapper for nsDragAndDrop.dragDropSecurityCheck
  150.     _securityCheck: function( aEvent, aDragSession, aDraggedText )
  151.     {
  152.         if ("dragDropSecurityCheck" in nsDragAndDrop)
  153.             nsDragAndDrop.dragDropSecurityCheck(aEvent, aDragSession, aDraggedText);
  154.         else if ("dragDropSecurityCheck" in gBrowser)
  155.             gBrowser.dragDropSecurityCheck(aEvent, aDragSession, aDraggedText);
  156.     },
  157.  
  158.     // Determine if two DOM nodes are from the same content area.
  159.     _fromSameContentArea: function( node1, node2 )
  160.     {
  161.         return(
  162.             node1 && node1.ownerDocument && node1.ownerDocument.defaultView &&
  163.             node2 && node2.ownerDocument && node2.ownerDocument.defaultView &&
  164.             node1.ownerDocument.defaultView.top.document ==
  165.             node2.ownerDocument.defaultView.top.document
  166.         );
  167.     },
  168.  
  169.     // Is this an event that we want to handle?
  170.     _shouldHandleEvent: function( evt )
  171.     {
  172.         return(
  173.             ( this._session.isDataFlavorSupported("text/unicode") ||
  174.               this._session.isDataFlavorSupported("text/plain") ) &&
  175.             ( this._session.sourceNode == null ||
  176.               this._fromSameContentArea(this._session.sourceNode, evt.target) )
  177.         );
  178.     },
  179.  
  180.     /**
  181.      * Event handlers
  182.      **/
  183.  
  184.     dragstart: function( evt )
  185.     {
  186.         this._sourceNode = evt.explicitOriginalTarget;
  187.     },
  188.  
  189.     dragover: function( evt )
  190.     {
  191.         if (!this._shouldHandleEvent(evt)) return;
  192.  
  193.         this._session.canDrop = true;
  194.     },
  195.  
  196.     dragdrop: function( evt )
  197.     {
  198.         if (!this._shouldHandleEvent(evt)) return;
  199.  
  200.         // Load preferences; note that the pref is FG, but the var is BG
  201.         const prefs = Components.classes["@mozilla.org/preferences-service;1"].
  202.                       getService(Components.interfaces.nsIPrefService).
  203.                       getBranch("extensions.quickdrag.");
  204.  
  205.         var loadLinkInBG = !prefs.getBoolPref("loadLinkInFG") ^ evt.shiftKey;
  206.         var loadSearchInBG = !prefs.getBoolPref("loadSearchInFG") ^ evt.shiftKey;
  207.         var downloadImages = prefs.getBoolPref("downloadImages") || evt.altKey;
  208.         var linkOpenOverride = prefs.getBoolPref("linkOpenOverride");
  209.  
  210.         // Get the source node and name
  211.         var sourceNode = this._session.sourceNode;
  212.  
  213.         if (this._sourceNode)
  214.         {
  215.             sourceNode = this._sourceNode;
  216.             this._sourceNode = null;
  217.         }
  218.  
  219.         var sourceName = (sourceNode) ? sourceNode.nodeName : "";
  220.  
  221.         // Flags
  222.         var isURI = false;
  223.         var isImage = false;
  224.         var isAnchorLink = false;
  225.  
  226.         // Parse the drag data
  227.         var dragData = this._getDragData(evt);
  228.         var lines = dragData.data.replace(/^\s+|\s+$/g, "").split(/\s*\n\s*/);
  229.         var str = lines.join(" ");
  230.  
  231.         if (dragData.type == "text/x-moz-url")
  232.         {
  233.             // The user has dragged either a link or an image
  234.  
  235.             // By default, we want to use the URI (the first line)
  236.             str = lines[0];
  237.             isURI = true;
  238.  
  239.             if (sourceName == "IMG")
  240.             {
  241.                 // Image or image link
  242.                 isImage = true;
  243.  
  244.                 // If the URI does not match the source node, then this is a
  245.                 // linked image (note that we DO want to treat images linked to
  246.                 // themselves as if they are not linked at all)
  247.                 if (sourceNode.src != str)
  248.                     isAnchorLink = true;
  249.             }
  250.             else if (sourceName == "#text")
  251.             {
  252.                 // Text link
  253.                 isAnchorLink = true;
  254.  
  255.                 // The link's content text, condensed into one line
  256.                 var text = lines.slice(1).join(" ");
  257.  
  258.                 // If appropriate, use the content text instead of the URI
  259.                 if (!linkOpenOverride && text)
  260.                 {
  261.                     str = text;
  262.                     isURI = false;
  263.                 }
  264.             }
  265.         }
  266.  
  267.         // Abort if we have no data; otherwise, proceed with URI detection
  268.         if (!str) return;
  269.  
  270.         // Our heuristics; see bug 58 for info about the http fixup
  271.         var hasScheme = /^(?:(?:h?tt|hxx)ps?|ftp|chrome|file):\/\//i;
  272.         var hasIP = /(?:^|[\/@])(?:\d{1,3}\.){3}\d{1,3}(?:[:\/\?]|$)/;
  273.         var hasDomain = new RegExp(
  274.             // starting boundary
  275.             "(?:^|[:\\/\\.@])" +
  276.             // valid second-level name
  277.             "[a-z0-9](?:[a-z0-9-]*[a-z0-9])" +
  278.             // valid top-level name: ccTLDs + hard-coded [gs]TLDs
  279.             "\\.(?:[a-z]{2}|aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel)" +
  280.             // end boundary
  281.             "(?:[:\\/\\?]|$)",
  282.             // ignore case
  283.             "i"
  284.         );
  285.  
  286.         isURI = isURI || hasScheme.test(str);
  287.         isURI = isURI || (!/\s/.test(str) && (hasIP.test(str) || hasDomain.test(str)));
  288.  
  289.         if (isURI)
  290.         {
  291.             // The scheme fixup here is more relaxed; patterns that match this
  292.             // fixup but that failed the initial scheme heuristic are those
  293.             // that match a valid domain or IP address pattern
  294.             str = str.replace(/^(?:t?t|h[tx]{2,})p(s?:\/\/)/i, "http$1");
  295.  
  296.             // Call dragDropSecurityCheck
  297.             this._securityCheck(evt, this._session, str);
  298.  
  299.             // Treat drag as a middle click?
  300.             var mimicMiddleClick = isAnchorLink && linkOpenOverride;
  301.  
  302.             // Send the referrer only for embedded images or emulated
  303.             // middle clicks over HTTP/HTTPS
  304.             var referrer = null;
  305.  
  306.             if (sourceNode)
  307.             {
  308.                 referrer = Components.classes["@mozilla.org/network/io-service;1"].
  309.                            getService(Components.interfaces.nsIIOService).
  310.                            newURI(sourceNode.ownerDocument.location.href, null, null);
  311.  
  312.                 if (!((isImage || mimicMiddleClick) && /^https?$/i.test(referrer.scheme)))
  313.                     referrer = null;
  314.             }
  315.  
  316.             // Turn naked e-mail addresses into mailto: links
  317.             if (/^[\w\.\+\-]+@[\w\.\-]+\.[\w\-]{2,}$/.test(str))
  318.                 str = "mailto:" + str;
  319.  
  320.             // For image links, the we want to use the source URL unless we
  321.             // are going to treat the image as a link
  322.             if (isImage && (!mimicMiddleClick || evt.ctrlKey))
  323.                 str = sourceNode.src;
  324.  
  325.             if (isImage && !mimicMiddleClick && !evt.ctrlKey && downloadImages)
  326.                 this._saveImage(str, referrer);
  327.             else if (!evt.altKey || (isImage && evt.ctrlKey) || !this._saveURL(str, referrer))
  328.                 this._loadTab(str, referrer, null, loadLinkInBG);
  329.         }
  330.         else if (this._isSuite)
  331.         {
  332.             // Suite search
  333.             OpenSearch('qdsearch', str, true, evt.shiftKey);
  334.         }
  335.         else
  336.         {
  337.             // Firefox search
  338.             // Based on BrowserSearch::loadSearch in browser.js
  339.  
  340.             const ss = Components.classes["@mozilla.org/browser/search-service;1"].
  341.                        getService(Components.interfaces.nsIBrowserSearchService);
  342.  
  343.             // Test to see if the search bar is active
  344.             var searchBarActive = false;
  345.             var searchBar = document.getElementById("searchbar");
  346.             if (searchBar)
  347.             {
  348.                 var style = window.getComputedStyle(searchBar.parentNode, null);
  349.                 if (style.visibility == "visible" && style.display != "none")
  350.                     searchBarActive = true;
  351.             }
  352.  
  353.             // If the search bar is visible, use the current engine;
  354.             // otherwise, fall back to the default engine
  355.             var engine = (searchBarActive) ? ss.currentEngine : ss.defaultEngine;
  356.             var submission = engine.getSubmission(str, null);
  357.  
  358.             // Open the search in a new tab
  359.             this._loadTab(submission.uri.spec, null, submission.postData, loadSearchInBG);
  360.         }
  361.  
  362.         evt.preventDefault();
  363.         evt.stopPropagation();
  364.     },
  365.  
  366.     /**
  367.      * SeaMonkey compatibility
  368.      **/
  369.  
  370.     // 0 == unknown, 1 == default (Firefox), 2 == suite (SeaMonkey)
  371.     _browserType: 0,
  372.  
  373.     // true == SeaMonkey, false == Firefox
  374.     get _isSuite( )
  375.     {
  376.         if (this._browserType == 0)
  377.         {
  378.             const xai = Components.classes["@mozilla.org/xre/app-info;1"].
  379.                         getService(Components.interfaces.nsIXULAppInfo);
  380.  
  381.             // Cache the result
  382.             this._browserType = (xai.ID == "{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}") ? 2 : 1;
  383.         }
  384.  
  385.         return(this._browserType == 2);
  386.     },
  387.  
  388.     // Wrapper for loadOneTab/addTab
  389.     _loadTab: function( aURI, aReferrerURI, aPostData, aLoadInBackground )
  390.     {
  391.         if (this._isSuite)
  392.             gBrowser.addTab(aURI, aReferrerURI, null, !aLoadInBackground, 0);
  393.         else
  394.             gBrowser.loadOneTab(aURI, aReferrerURI, null, aPostData, aLoadInBackground, false);
  395.     },
  396.  
  397.     // Wrapper for saveURL
  398.     _saveURL: function( aURL, aReferrer )
  399.     {
  400.         // Unlike _loadTab, we need to do the scheme fixup manually; the "." is
  401.         // omitted because example.org:80 is probably server:80, not scheme:80
  402.         if (!/^[a-z][\da-z+\-]*:/i.test(aURL))
  403.             aURL = aURL.replace(/^:*[\/\\\s]*/, "http://").replace(/^ht(tp:\/\/ftp\.)/i, "f$1");
  404.  
  405.         // If the protocol is not supported, let it fall through to a new tab
  406.         if (!/^(?:https?|ftp):/i.test(aURL))
  407.             return(false);
  408.  
  409.         if (this._isSuite)
  410.             saveURL(aURL, null, null, false, aReferrer);
  411.         else
  412.             saveURL(aURL, null, null, false, true, aReferrer);
  413.  
  414.         return(true);
  415.     },
  416.  
  417.     // Wrapper for saveImageURL
  418.     _saveImage: function( aURL, aReferrer )
  419.     {
  420.         if (this._isSuite)
  421.             saveImageURL(aURL, null, null, false, aReferrer);
  422.         else
  423.             saveImageURL(aURL, null, null, false, true, aReferrer);
  424.     }
  425. };
  426.  
  427. window.addEventListener("load", QuickDragListener, false);
  428. window.addEventListener("unload", QuickDragListener, false);
  429.